\chapter{表达式}\label{ch06}

\emph{LISP programmers know the value of everything, but the cost of nothing}

\begin{flushright}
    ——Alan Perlis, epigram \#55
\end{flushright}

在本章中，我们将介绍Rust的\emph{表达式}，它是构成Rust函数体和大部分Rust代码的构建块。本章将探索表达式的力量以及如何克服它的局限。我们还将介绍控制流，它在Rust中完全是以表达式为基础的，最后还要介绍Rust中的基本运算符如何单独和组合工作。

还有一些从技术角度应该划入这一类的概念，例如闭包和迭代器。但它们太过重要因此我们之后会用单独的章节介绍它们。现在，我们希望能用尽可能少的页数介绍尽可能多的语法。

\section{表达式语言}

Rust表面上看上去像C家族的语言，但这其实是一个误解。在C语言中，\emph{表达式}和\emph{语句}之间有很大的不同。表达式是一些像这样的代码：
\begin{minted}{C}
    5 * (fahr-32) / 9
\end{minted}
而语句则是像这样的：
\begin{minted}{C}
    for (; begin != end; ++begin) {
        if (*begin == target)
            break;
    }
\end{minted}
表达式有值，但语句没有。

Rust是一种\emph{表达式语言}。这意味着它遵循了起源于Lisp的传统，即表达式负责完成所有工作。

在C中，\texttt{if}和\texttt{switch}是语句。它们并不产生值，也不能被用在表达式中间。在Rust中，\texttt{if}和\texttt{match}\emph{可以}产生值。我们已经在\hyperref[ch02]{第2章}中看到过一个产生数字值的\texttt{match}表达式：
\begin{minted}{Rust}
    pixels[r * bounds.0 + c] =
        match escapes(Complex { re: point.0, im: point.1 }, 255) {
            None => 0,
            Some(count) => 255 - count as u8
        };
\end{minted}

一个\texttt{if}表达式可以用于初始化一个变量：
\begin{minted}{Rust}
    let status =
        if cpu.temperature <= MAX_TEMP {
            HttpStatus::Ok
        } else {
            HttpStatus::ServerError  // server melted
        };
\end{minted}

一个\texttt{match}表达式可以被用作函数或宏的参数：
\begin{minted}{Rust}
    println!("Inside the vat, you see {}.",
        match vat.contents {
            Some(brain) => brain.desc(),
            None => "nothing of interest"
        });
\end{minted}

这解释了Rust为什么没有C的三元运算符\texttt{(expr1 ? expr2 : expr3)}。在C中，它是一种类似\texttt{if}语句的表达式。但在Rust中这种写法是多余的，因为\texttt{if}表达式可以同时实现这两种功能。

C中的大部分控制流工具都是语句，而Rust中的控制流则全是表达式。

\section{优先级与结合性}

\hyperref[t6-1]{表6-1}总结了Rust的表达式语法。我们将在这一章中介绍所有这些表达式。运算符按照优先级从高到低的顺序列出。（类似于大多数编程语言，Rust使用\emph{运算符优先级}来决定当表达式中含有多个运算符时的运算顺序。例如，在表达式\texttt{limit < 2 * broom.size + 1}中，\texttt{.}运算符优先级最高，因此会先访问字段。）

\begin{longtable}{p{0.25\textwidth}p{0.35\textwidth}p{0.3\textwidth}}
    \caption{表达式}
    \label{t6-1} \\
    \hline
    \textbf{表达式类型} & \textbf{示例} & \textbf{相关trait} \\
    \hline
    数组字面量      & \texttt{[1, 2, 3]}         & \\
    重复数组字面量  & \texttt{[0; 50]}           & \\
    元组            & \texttt{(6, "crullers")}  & \\
    \cline{1-2}
    组合            & \texttt{(2 + 2)}             & \\
    块              & \texttt{\{ f(); g() \}}      & \\
    控制流表达式     & \texttt{if ok \{ f() \}}     & \\
                    & \texttt{if ok \{ 1 \} else \{ 0 \}}                   & \\
                    & \texttt{if let Some(x) = f() \{ x \} else \{ 0 \}}    & \\
                    & \texttt{match x \{ None => 0, \_ => 1 \}}             & \\
                    & \texttt{for v in e \{ f(v); \}}                       & \texttt{\hyperref[iter]{std::iter::IntoIterator}} \\
                    & \texttt{while ok \{ ok = f(); \}}                     & \\
                    & \texttt{while let Some(x) = it.next() \{ f(x); \}}    & \\
                    & \texttt{loop \{ next\_event(); \}}                    & \\
                    & \texttt{break}                  & \\
                    & \texttt{continue}               & \\
                    & \texttt{return 0}               & \\
    宏调用          & \texttt{println!("ok")}         & \\
    路径            & \texttt{std::f64::consts::PI}   & \\
    \cline{1-2}
    结构体字面量     & \texttt{Point \{x: 0, y: 0\}}     & \\
    \cline{1-2}
    元组字段访问    & \texttt{pair.0}   & \texttt{\hyperref[deref]{Deref}, \hyperref[deref]{DerefMut}} \\
    结构体字段访问  & \texttt{point.x}  & \texttt{\hyperref[deref]{Deref}, \hyperref[deref]{DerefMut}} \\
    方法调用       & \texttt{point.translate(50, 50)} & \texttt{\hyperref[deref]{Deref}, \hyperref[deref]{DerefMut}} \\
    函数调用       & \texttt{stdin()}   & \texttt{\hyperref[fn]{Fn(Arg0, ...) -> T}, \hyperref[fn]{FnMut(Arg0, ...) -> T}, \hyperref[fn]{FnOnce(Arg0, ...) -> T}} \\
    索引            & \texttt{arr[0]}   & \texttt{\hyperref[index]{Index}, \hyperref[index]{IndexMut}, \hyperref[deref]{Deref}, \hyperref[deref]{DerefMut}} \\
    \cline{1-2}
    错误检查        & \texttt{create\_dir("tmp")?}   & \\
    \cline{1-2}
    逻辑/位 NOT     & \texttt{!ok}  & \texttt{\hyperref[unop]{Not}} \\
    负             & \texttt{-num}  & \texttt{\hyperref[unop]{Neg}} \\
    解引用          & \texttt{*ptr} & \texttt{\hyperref[deref]{Deref}, \hyperref[deref]{DerefMut}} \\
    借用            & \texttt{\&val}    & \\
    \cline{1-2}
    类型转换    & \texttt{x as u32} & \\
    \cline{1-2}
    乘法        & \texttt{n * 2}    & \texttt{\hyperref[biop]{Mul}} \\
    除法        & \texttt{n / 2}    & \texttt{\hyperref[biop]{Div}} \\
    余数（取模） & \texttt{n \% 2}   & \texttt{\hyperref[biop]{Rem}} \\
    \hline
    加法        & \texttt{n + 1}    & \texttt{\hyperref[biop]{Add}} \\
    减法        & \texttt{n - 1}    & \texttt{\hyperref[biop]{Sub}} \\
    \hline
    左移        & \texttt{n << 1}   & \texttt{\hyperref[biop]{Shl}} \\
    右移        & \texttt{n >> 1}   & \texttt{\hyperref[biop]{Shr}} \\
    \hline
    位与        & \texttt{n \& 1}   & \texttt{\hyperref[biop]{BitAnd}} \\
    \hline
    位异或      & \texttt{n \^{} 1} & \texttt{\hyperref[biop]{BitXor}} \\
    \hline
    位或        & \texttt{n | 1}    & \texttt{\hyperref[biop]{BitOr}}  \\
    \hline
    小于        & \texttt{n < 1}    & \texttt{\hyperref[cmp]{std::cmp::PartialOrd}} \\
    小于等于    & \texttt{n <= 1}   & \texttt{\hyperref[cmp]{std::cmp::PartialOrd}} \\
    大于        & \texttt{n > 1}    & \texttt{\hyperref[cmp]{std::cmp::PartialOrd}} \\
    大于等于    & \texttt{n >= 1}   & \texttt{\hyperref[cmp]{std::cmp::PartialOrd}} \\
    等于        & \texttt{n == 1}   & \texttt{\hyperref[equal]{std::cmp::PartialEq}} \\
    不等于      & \texttt{n != 1}   & \texttt{\hyperref[equal]{std::cmp::PartialEq}} \\   
    \hline
    逻辑与      & \texttt{x.ok \&\& y.ok}       & \\
    \cline{1-2}
    逻辑或      & \texttt{x.ok || backup.ok}    & \\
    \cline{1-2}
    左闭右开区间 & \texttt{start .. stop}   & \\
    左闭右闭区间 & \texttt{start ..= stop}  & \\
    \cline{1-2}
    赋值        & \texttt{x = val}  & \\
    复合赋值    & \texttt{x *= 1}   & \texttt{\hyperref[assign]{MulAssign}} \\
                & \texttt{x /= 1}   & \texttt{\hyperref[assign]{DivAssign}} \\
                & \texttt{x \%= 1}  & \texttt{\hyperref[assign]{RemAssign}} \\
                & \texttt{x += 1}   & \texttt{\hyperref[assign]{AddAssign}} \\
                & \texttt{x -= 1}   & \texttt{\hyperref[assign]{SubAssign}} \\
                & \texttt{x <<= 1}  & \texttt{\hyperref[assign]{ShlAssign}} \\
                & \texttt{x >>= 1}  & \texttt{\hyperref[assign]{ShrAssign}} \\
                & \texttt{x \&= 1}  & \texttt{\hyperref[assign]{BitAndAssign}} \\
                & \texttt{x \^{}= 1}& \texttt{\hyperref[assign]{BitXorAssign}} \\
                & \texttt{x |= 1}   & \texttt{\hyperref[assign]{BitOrAssign}} \\
    \cline{1-2}
    闭包        & \texttt{|x, y| x + y} & \\
\end{longtable}

所有可以链式使用的运算符都是左结合的。也就是说，一条运算链例如\texttt{a - b - c}被组合为\texttt{(a - b) - c}，而不是\texttt{a - (b - c)}。这些运算符可以被任意组合：
\begin{minted}{text}
    * / % + - << >> & ^ | && || as
\end{minted}
比较运算符、赋值运算符、范围运算符\texttt{..}和\texttt{..=}不能被链式使用。

\section{块和分号}

块是最通用的表达式。一个块产生一个值，可以被用于任何需要一个值的地方：
\begin{minted}{Rust}
    let display_name = match post.author() {
        Some(author) => author.name(),
        None => {
            let network_info = post.get_network_metadata()?;
            let ip = network_info.client_address();
            ip.to_string()
        }
    };
\end{minted}
\texttt{Some(author) =>}之后的代码是简单的表达式\texttt{author.name()}，\texttt{None =>}之后的代码则是一个块表达式。对Rust来种，两种表达式没有区别。块表达式的值是它的最后一条表达式的值，也就是\texttt{ip.to\_string()}。

注意\texttt{ip.to\_string()}后面没有分号。Rust中的大部分代码行都以分号或者花括号结尾，类似于C和Java。如果一个块看起来像C代码一样在所有的表达式后边都有分号，那它的行为就和C块一样，它的值将是\texttt{()}。正如我们在\hyperref[ch02]{第2章}提到的，当你省略了块中最后一个表达式后边的分号，那么块的值将是最后一个表达式的值，而不是通常的\texttt{()}。

在一些语言中，尤其是Javascript，你可以省略分号，语言会自动为你添加上——这样除了方便一点，并没有任何区别。然而在Rust中，分号通常是有实际意义的：
\begin{minted}{Rust}
    let msg = {
        // let语句：总是需要分号
        let dandelion_control = puffball.open();

        // 表达式 + 分号：方法被调用，返回值被丢弃
        dandelion_control.release_all_seeds(launch_codes);

        // 没有分号的表达式：方法被调用，
        // 返回值被存储到 `msg`
        dandelion_control.get_status()
    };
\end{minted}

语句块可以包含声明最后还能产生一个值的能力是一个很有用的特性，而且可以很快习惯。它的一个缺陷是如果你偶然忘记了分号会导致一条错误信息：
\begin{minted}{Rust}
    ...
    if preferences.changed() {
        page.compute_size()  // oops, 缺少分号
    }
\end{minted}

如果你在C或者Java程序中犯了这种错误，编译器会简单地直接指出你少写了一个分号。然而这是Rust的报错：
\begin{minted}{text}
    error[E0308]: mismatched types
    22 |         page.compute_size()  // oops, missing semicolon
       |         ^^^^^^^^^^^^^^^^^^^- help: try adding a semicolon `;`
       |         |
       |         expected (), found tuple
       |
       = note: expected unit type `()`
                  found tuple `(u32, u32)`
\end{minted}

在缺少分号的情况下，块的值将是\texttt{page.compute\_size()}返回的值，但一个没有\texttt{else}分支的\texttt{if}语句必须总是返回\texttt{()}。幸运的是，Rust知道这种类型的错误并建议加上分号。

\section{声明}\label{declaration}

除了表达式和分号之外，一个块中可能包含任意数量的声明。最常见的情况是\texttt{let}声明，它用来声明局部变量：
\begin{minted}{Rust}
    let name: type = expr;
\end{minted}

类型和初始值是可选的，分号是必须的。

一个\texttt{let}声明可以在不初始化的情况下声明一个变量。这有时很有用，因为有时候一个变量需要在控制流的中途初始化：
\begin{minted}{Rust}
    let name;
    if user.has_nickname() {
        name = user.nickname();
    } else {
        name = generate_unique_name();
        user.register(&name);
    }
\end{minted}

局部变量\texttt{name}有两种不同的初始化路径，但两条路径上它都只会被初始化一次，所以\texttt{name}不需要声明为\texttt{mut}。

在变量初始化之前使用它会导致错误（这和使用被move的值的错误紧密相关，Rust希望你只在变量的值存在时使用它们！）。

你有时可能会看到代码似乎重新声明一个已经存在的变量，例如：
\begin{minted}{Rust}
    for line in file.lines() {
        let line = line?;
        ...
    }
\end{minted}

\texttt{let}声明创建了一个新的、类型不同的、第二个变量\texttt{line}。第一个\texttt{line}的类型是\\
\texttt{Result<String, io::Error>}，第二个\texttt{line}是一个\texttt{String}。第二个声明在块的剩余部分会取代第一个。这被称为\emph{遮蔽(shadowing)}，在Rust程序中非常常见。上面的代码等价于：
\begin{minted}{Rust}
    for line_result in file.lines() {
        let line = line_result?;
        ...
    }
\end{minted}

在本书中，我们将坚持在这种场景中使用\texttt{\_result}后缀，来保证变量的名字不同。

一个块还可以包含\emph{item declarations}。一个item是一个可以出现在全局或模块中的声明，例如\texttt{fn}、\texttt{struct}、\texttt{use}。

后面的章节将会详细介绍item。现在，\texttt{fn}足够作为一个例子了。任何块都可以包含\texttt{fn}声明：
\begin{minted}{Rust}
    use std::io;
    use std::cmp::Ordering;

    fn show_files() -> io::Result<()> {
        let mut v = vec![];
        ...
        fn cmp_by_timestamp_then_name(a: &FileInfo, b: &FileInfo) -> Ordering {
            a.timestamp.cmp(&b.timestamp)   // 首先，比较时间戳 
                .reverse()                  // 最新的文件优先
                .then(a.path.cmp(&b.path))  // 比较路径
        }

        v.sort_by(cmp_by_timestamp_then_name);
        ...
    }
\end{minted}

当一个\texttt{fn}在块内声明的时候，它的作用域是整个块，它可以在整个块内\emph{使用}。但是一个嵌套的\texttt{fn}不能访问外围作用域的局部变量和参数。例如，函数\texttt{cmp\_by\_timestamp\_then\_name}不能使用\texttt{v}。（Rust还有闭包，闭包可以使用外层作用域的变量，见\hyperref[ch14]{第14章}。）

一个块甚至可以包含整个模块。这听起来可能有些多余了：我们真的需要把语言的\emph{每一部分}嵌套在其他部分中的能力吗？——但程序员（尤其是使用宏的程序员）可以找到语言提供的每一个正交碎片的用法。

\section{\texttt{if}与\texttt{match}}

\texttt{if}表达式的形式大家都很熟悉：
\begin{minted}{Rust}
    if condition1 {
        block1
    } else if condition2 {
        block2
    } else {
        block_n
    }
\end{minted}

每一个\texttt{condition}都必须是\texttt{bool}类型的表达式，Rust不会隐式地将数字或者指针类型转换为布尔值。

和C不同的是条件表达式不需要括号，事实上如果有不必要的括号的话，\texttt{rustc}会发出警告。不过花括号是必须的。

\texttt{else if}块，和最后的\texttt{else}都是可选的。一个没有\texttt{else}块的\texttt{if}表达式类似于一个\texttt{else}块为空的\texttt{if}表达式。

\texttt{match}表达式有些类似于C的\texttt{switch}语句，但是更加灵活。一个简单的例子如下：
\begin{minted}{Rust}
    match code {
        0 => println!("OK"),
        1 => println!("Wires Tangled"),
        2 => println!("User Asleep"),
        _ => println!("Unrecognized Error {}", code)
    }
\end{minted}
这类似于一个\texttt{switch}语句，\texttt{match}表达式的四条分支里只有一条会执行，取决于\texttt{code}的值。通配符模式\texttt{\_}可以匹配任何情况，类似于\texttt{switch}语句中的\texttt{default:}标签，不过它会覆盖之后的所有模式，它之后的模式将永远不会匹配到任何东西（出现这种情况时编译器也会警告你）。

编译器可以使用跳转表来优化这种\texttt{match}表达式，类似于C++中的\texttt{switch}语句。当\texttt{match}\\
的每个分支都返回常量值时还会有一个类似的优化，这种情况下，编译器会用那些值构建一个数组，然后\texttt{match}会被编译为一次数组访问，这种情况下除了边界检查之外，编译出的代码将不会有任何条件分支。

每个分支中\texttt{=>}左侧支持多种\emph{模式}，这是\texttt{match}功能强大的根源。上边的例子中，每种模式都只是一个简单的整数。我们还展示过区分\texttt{Option}的两种值的\texttt{match}表达式：
\begin{minted}{Rust}
    match params.get("name") {
        Some(name) => println!("Hello, {}!", name);
        None => println!("Greetings, stranger.");
    }
\end{minted}

这只是模式的一个小应用，一个模式可以匹配很多值。它可以解包元组，可以匹配结构体中的每个字段，可以解引用，借用一个值的一部分，等等。Rust的模式是一种专门的mini语言。我们将在\hyperref[ch10]{第10章}中介绍它们。

\texttt{match}表达式的通用形式是：
\begin{minted}{Rust}
    match value {
        pattern => expr,
        ...
    }
\end{minted}
如果\texttt{expr}是一个块的话，分支最后的逗号可以省略。

Rust会从第一个分支开始，逐个检查\texttt{value}和给定的模式是否匹配。当有一个模式匹配时，相应的\texttt{expr}将会被求值，整个\texttt{match}表达式将完成执行，不会再检查别的模式。必须至少有一个模式可以匹配，Rust会禁止没有覆盖所有可能情况的\texttt{match}表达式：
\begin{minted}{Rust}
    let score = match card.rank {
        Jack => 10,
        Queen => 10,
        Ace => 11
    };  // 错误：没有穷尽所有模式
\end{minted}

\texttt{if}表达式的所有块必须产生相同类型的值：
\begin{minted}{Rust}
    let suggested_pet = 
        if with_wings { Pet::Buzzard } else { Pet::Hyena }; // ok
    let favorite_number =
        if user.is_hobbit() { "eleventy-one" } else { 9 };  // error
    let best_sports_team = 
        if is_hockey_season() { "Predators" };  // error
\end{minted}
（最后一个例子会导致错误，因为在7月结果将是\texttt{()}。）\footnote{译者注：7月不是曲棍球赛季？}

类似的，\texttt{match}表达式的分支也必须有相同的类型：
\begin{minted}{Rust}
    let suggested_pet =
        match favorite.element {
            Fire => Pet::RedPanda,
            Air => Pet::Buffalo,
            Water => Pet::Orca,
            _ => None   // 错误：类型不一致
        }
\end{minted}

\section{\texttt{if let}}\label{iflet}

\texttt{if}表达式还有一种形式：\texttt{if let}表达式：
\begin{minted}{Rust}
    if let pattern = expr {
        block1
    } else {
        block2
    }
\end{minted}

\texttt{expr}如果能匹配\texttt{pattern}，则执行\texttt{block1}，如果不能匹配，则执行\texttt{block2}。有时这是一种获取\texttt{Option}或\texttt{Result}的值的好方法：
\begin{minted}{Rust}
    if let Some(cookie) = request.session_cookie {
        return restore_session(cookie);
    }

    if let Err(err) = show_cheesy_anti_robot_task() {
        log_robot_attempt(err);
        politely_accuse_user_of_being_a_robot();
    } else {
        session.mark_as_human();
    }
\end{minted}

没有任何场景\emph{必须}使用\texttt{if let}，因为\texttt{match}可以做到任何\texttt{if let}可以做的事。\texttt{if let}表达式类似于如下只有一个模式的\texttt{match}表达式的缩写：
\begin{minted}{Rust}
    match expr {
        pattern => { block1 }
        _ => { block2 }
    }
\end{minted}

\section{循环}\label{loop}

有四种循环表达式：
\begin{minted}{Rust}
    while condition {
        block
    }
    
    while let pattern = expr {
        block
    }

    loop {
        block
    }

    for pattern in iterable {
        block
    }
\end{minted}

在Rust中循环也是表达式，不过\texttt{while}或\texttt{for}循环的值总是\texttt{()}，因此它们的值并没有用。如果你指明的话\texttt{loop}表达式可以产生一个值。

\texttt{while}循环的行为和C语言中的基本一样，除了\texttt{condition}必须是精确的\texttt{bool}类型的表达式。

\texttt{while let}循环类似于\texttt{if let}。每一次迭代循环开始时，如果\texttt{expr}的值可以匹配\texttt{pattern}，将会运行表达式块，否则循环将结束。

使用\texttt{loop}循环来编写无限循环。它会永远重复执行\texttt{block}（直到遇到\texttt{break}或\texttt{return}或线程panic）。

一个\texttt{for}循环会先求值\texttt{iterable}表达式，然后对表达式返回的迭代器产生的每个值执行一次\texttt{block}。很多类型都可以被迭代，包括所有标准集合例如\texttt{Vec}和\texttt{HashMap}。标准的C语言中的\texttt{for}循环：
\begin{minted}{Rust}
    for (int i = 0; i < 20; i++) {
        printf("%d\n", i);
    }
\end{minted}

用Rust来写的话就是：
\begin{minted}{Rust}
    for i in 0..20 {
        println!("{}", i);
    }
\end{minted}
和C语言一样，最后一个打印出的数字是\texttt{19}。

\texttt{..}运算符会产生一个\emph{范围}，它是一个只有两个字段\texttt{start}和\texttt{end}的简单结构体。\texttt{0..20}等价于\texttt{std::ops::Range \{ start: 0, end: 20 \}}。范围可以用于\texttt{for}循环，因为\texttt{Range}是可以迭代的类型，它实现了\texttt{std::iter::IntoIterator} trait，我们将在\hyperref[ch15]{第15章}中讨论这些。标准集合都是可迭代的，数组和切片也是。

为了保持Rust中的移动语义，\texttt{for}循环会消耗掉值：
\begin{minted}{Rust}
    let strings: Vec<String> = error_messages();
    for s in strings {          // 每一个String被移动进s
        println!("{}", s);
    }                           // 并在这里drop
    println!("{} error(s)", strings.len()); // 错误：使用了被move的值
\end{minted}

这样可能会很不方便，一个简单的方法是让循环获取集合的引用。然后循环变量将会变成集合中每一个元素的引用：
\begin{minted}{Rust}
    for rs in &strings {
        println!("String {:?} is at address {:P}.", *rs, rs);
    }
\end{minted}

这里\texttt{\&strings}的类型是\texttt{\&Vec<String>}，\texttt{rs}的类型是\texttt{\&String}。

迭代一个\texttt{mut}引用会产生每个元素的\texttt{mut}引用：
\begin{minted}{Rust}
    for rs in &mut strings {    // rs的类型是&mut String
        rs.push('\n');  // 每个字符串添加一个换行符
    }
\end{minted}

\hyperref[ch15]{第15章}会更详细的介绍\texttt{for}循环并展示其他使用迭代器的方式。

\section{循环中的控制流}

\texttt{break}表达式用来跳出循环（Rust中\texttt{break}只能在循环中使用。\texttt{match}表达式中不需要它，这一点和\texttt{switch}语句不同）。

在一个\texttt{loop}循环体内，你可以给\texttt{break}一个表达式，
表达式的值就是整个循环的值：
\begin{minted}{Rust}
    // 每一次对`next_line`的调用返回`Some(line)`，其中`line`
    // 是输入的一行；或者返回`None`，表示已经到达输入结尾。
    // 返回第一个以"answer: "开头的行。
    // 如果没有，就返回"answer: nothing"。
    let answer = loop {
        if let Some(line) = next_line() {
            if line.starts_with("answer: ") {
                break line;
            }
        } else {
            break "answer: nothing";
        }
    };
\end{minted}

自然，\texttt{loop}表达式中的所有\texttt{break}表达式必须产生相同类型的值，这个类型也会成为\texttt{loop}本身的类型。

一个\texttt{continue}表达式跳转到下一次迭代：
\begin{minted}{Rust}
    // 读取一些数据，一次读取一行
    for line in input_lines {
        let trimmed = trim_comments_and_whitespace(line);
        if trimmed.is_empty() {
            // 跳转到loop的开头并
            // 移动到下一行输入
            continue;
        }
        ...
    }
\end{minted}

在一个\texttt{for}循环中，\texttt{continue}会跳转到集合中的下一个值，如果没有值了，循环会停止。类似的，在一个\texttt{while}循环中\texttt{continue}会重新检查循环条件，如果现在是false，循环会停止。

一个循环可以用一个生命周期\emph{标记}。在下面的例子中，\texttt{'search}是一个外层\texttt{for}循环的标签。因此\texttt{break 'search}会退出外层循环，而不是内层循环：
\begin{minted}{Rust}
    'search:
    for room in apartment {
        for spot in room.hiding_spots() {
            if spot.contains(keys) {
                println!("Your keys are {} in the {}.", spot, room);
                break 'search;
            }
        }
    }
\end{minted}

一个\texttt{break}可以同时带有一个标签和一个值表达式：
\begin{minted}{Rust}
    // 寻找一列数中的第一个完全平方
    let sqrt = 'outer: loop {
        let n = next_number();
        for i in 1.. {
            let square = i * i;
            if square == n {
                // 找到了一个平方根
                break 'outer i;
            }
            if square > n {
                // `n`不是完全平方，尝试下一个数
                break;
            }
        }
    };
\end{minted}

标签也可以和\texttt{continue}一起使用。

\section{\texttt{return}表达式}
\texttt{return}表达式退出当前函数，向调用者返回一个值。

没有值的\texttt{return}相当于\texttt{return ()}：
\begin{minted}{Rust}
    fn f() {    // 返回类型被省略：默认是()
        return; // 返回值被省略：默认是()
    }
\end{minted}

函数并不一定需要显式的\texttt{return}表达式。函数体就像是一个块表达式：如果最后一个表达式后边没有分号，它的值将是函数的返回值。事实上，这是在Rust中充当返回值的最佳方法。

但这并不意味着\texttt{return}就毫无作用，或者仅仅是为了符合不熟悉表达式语言的用户的习惯。类似于\texttt{break}表达式，\texttt{return}可以终止当前的工作。例如，在\hyperref[ch02]{第2章}中，在调用了一个可能会失败的函数之后，我们使用了\texttt{?}运算符来检查错误：
\begin{minted}{Rust}
    let output = File::create(filename)?;
\end{minted}

我们解释过这是\texttt{match}表达式的缩写：
\begin{minted}{Rust}
    let output = match File::create(filename) {
        Ok(f) => f,
        Err(err) => return Err(err)
    };
\end{minted}

这段代码首先调用\texttt{File::create(filename)}。如果返回\texttt{Ok(f)}，那么整个\texttt{match}表达式将求值为\texttt{f}，因此\texttt{f}被存储在\texttt{output}中，然后将继续执行\texttt{match}之后的代码。

否则， 我们会匹配到\texttt{Err(err)}，然后触发\texttt{return}表达式。此时我们正在求值一个\texttt{match}表达式，来决定变量\texttt{output}的值。但这都无所谓，我们会放弃所有任务，直接退出函数，并返回我们从\texttt{File::create()}得到的错误。

我们将在\hyperref[properror]{传播错误}一节中详细介绍\texttt{?}运算符。

\section{Rust为什么会有loop循环}

Rust编译器的几个部分会检查整个程序中的控制流：
\begin{itemize}
    \item Rust会检查一个函数里的所有返回路径是否返回相同类型的值。为了做到这一点，它需要知道控制流是否能到达函数结尾。
    \item Rust会检查未初始化的局部变量是否绝不会被使用。这需要检查程序中的每一条路径来确保没有路径会到达未初始化的变量被使用的情况。
    \item Rust会警告不可达的代码。函数中\emph{没有}路径可以到达的代码就是不可达的代码。
\end{itemize}

这些被称为\emph{控制流敏感(flow-sensitive)}分析。这并不是新的概念，Java很多年前就有一个“确定性赋值(definite assignment)”的分析，和Rust的检查很相似。

当强迫执行这种规则时，一门语言（的编译器）必须在简洁和智能之间权衡。简洁可以让程序员更容易地理解编译器在说什么；智能则可以帮助消除错误警告和程序完全正确但编译器却报错的情况。Rust倾向于简洁，它的控制流敏感分析根本就不检查循环条件，而是假设程序中的所有条件都既可能是真又可能是假。

这导致Rust会拒绝编译这样的安全程序：
\begin{minted}{Rust}
    fn wait_for_process(process: &mut Process) -> i32 {
        while true {
            if process.wait() {
                return process.exit_code();
            }
        }
    }   // 错误：不匹配的类型：期望i32，得到()
\end{minted}

这里的错误是假的。函数只可能通过\texttt{return}表达式返回，因此\texttt{while}循环不会产生\texttt{i32}值是无关紧要的。

而\texttt{loop}表达式作为一种“告诉编译器你的意思”的解决方案来解决这个问题。

Rust的类型系统也会被控制流影响。之前我们说过\texttt{if}表达式中的所有分支都必须有相同的类型。但如果在以\texttt{break}、\texttt{return}、\texttt{loop}表达式或对\texttt{panic!()}、\texttt{std::process::exit()}的调用作为结尾的块中强迫这个规则就会显得很愚蠢。这些表达式的共同点就是它们永远不会像正常的方式一样产生一个值：\texttt{break}或\texttt{return}会中断并退出当前的块、无限的\texttt{loop}循环则永远不会结束，等等。

因此在Rust中，这些表达式并没有通常的类型。不会正常结束的表达式会被赋予特殊类型\texttt{!}，并且它们不受类型必须匹配的规则的约束。 你可以在\texttt{std::process::exit()}的函数签名中看到\texttt{!}：
\begin{minted}{Rust}
    fn exit(code: i32) -> !
\end{minted}

\texttt{!}意味着\texttt{exit()}永远不会返回，它是一个\emph{发散函数(divergent function)}。

你可以用相同的语法编写自己的发散函数，在某些情况下这会显得非常自然：
\begin{minted}{Rust}
    fn serve_forever(socket: ServerSocket, handler: ServerHandler) -> ! {
        socket.listen();
        loop {
            let s = socket.accept();
            handler.handle(s);
        }
    }
\end{minted}

当然，如果函数能正常返回的话，Rust会认为这是一个错误。

有了这些大规模控制流的构建块之后，我们可以继续分析常用的更细粒度的表达式，例如函数调用和算术运算。

\section{函数和方法调用}

在Rust中调用函数的方法的语法和许多其它语言一样：
\begin{minted}{Rust}
    let x = gcd(1302, 462);         // 函数调用
    let room = player.location();   // 方法调用
\end{minted}

在第二个例子中，\texttt{player}是一个编造的\texttt{Player}类型的变量，它有一个\texttt{.location()}方法。（我们将在\hyperref[ch09]{第9章}中讨论自定义类型时展示如何定义自己的方法。）

Rust通常会严格区分引用和它指向的值。如果你向接收\texttt{i32}的函数传递\texttt{\&i32}，将会引发类型错误。不过你会注意到\texttt{.}运算符放宽了这个限制，在方法调用\texttt{player.location()}中，\texttt{player}可以是一个\texttt{Player}、也可以是一个引用类型\texttt{\&Player}、也可以是智能指针类型\\
\texttt{Box<Player>}或者\texttt{Rc<Player>}。\texttt{.location()}方法可能会以值也可能会以引用获取参数，同样的\texttt{.location()}语法可以适用于所有情况，因为Rust的\texttt{.}运算符会根据需要自动解引用\texttt{player}或者获取它的引用。

第三种语法用于调用类型关联函数，例如\texttt{Vec::new()}：
\begin{minted}{Rust}
    let mut numbers = Vec::new();   // 类型关联函数调用
\end{minted}

这类似于面向对象语言中的静态方法，方法在值上调用（例如\texttt{my\_vec.len()}），而类型关联函数在类型上调用（例如\texttt{Vec::new()}）。

当然，方法调用可以被串联起来：
\begin{minted}{Rust}
    // 取自于第二章的Actix-based web server
    server
        .bind("127.0.0.1:3000").expect("error binding server to address")
        .run().expect("error running server");
\end{minted}

Rust语法中的一个怪异之处是，不能按照泛型类型的通常语法例如\texttt{Vec<T>}来调用函数或者方法：
\begin{minted}{Rust}
    return Vec<i32>::with_capacity(1000);   // error: something about chained comparisons
    let ramp = (0 .. n).collect<Vec<i32>>();    // 同样的错误
\end{minted}

问题在于这个表达式中的\texttt{<}被当作小于运算符。Rust编译器会建议使用\texttt{::<T>}来代替\texttt{<T>}，这样可以解决这个问题：
\begin{minted}{Rust}
    return Vec::<i32>::with_capacity(1000); // ok, using ::<

    let ramp = (0 .. n).collect::<Vec<i32>>(); // ok, using ::<
\end{minted}

符号\texttt{::<...>}在Rust中被亲切地称为\emph{turbofish}。

另外，通常也可以省略类型参数，让Rust推断它们：
\begin{minted}{Rust}
    return Vec::with_capacity(10);  // ok, 如果函数的返回类型是Vec<i32>

    let ramp: Vec<i32> = (0 .. n).collect(); // ok, 指定了变量的类型
\end{minted}

在可以推断出类型时省略类型是一种好的风格。

\section{字段和元素}\label{field}

访问结构体中字段的语法大家都很熟悉，元组也一样，不过元组中的字段是数字而不是名称：
\begin{minted}{Rust}
    game.black_pawns    // 结构体字段
    coords.1            // 元组元素
\end{minted}

如果点左边的值是引用或者智能指针类型，它也会像方法调用一样自动解引用。

方括号可以访问数组、切片或vector中的元素：
\begin{minted}{Rust}
    pieces[i]           // 数组元素
\end{minted}

方括号左侧的值也会自动解引用。

类似于这三种的表达式被称为\emph{左值}，因为它们可以出现在赋值表达式的左边：
\begin{minted}{Rust}
    game.black_pawns = 0x00ff0000_00000000_u64;
    coords.1 = 0;
    pieces[2] = Some(Piece::new(Black, Knight, coords));
\end{minted}

当然，只有当\texttt{game}、\texttt{coords}和\texttt{pieces}被声明为\texttt{mut}变量时才可以这么做。

从一个数组或者vector中提取切片非常直观：
\begin{minted}{Rust}
    let second_half = &game_moves[midpoint .. end];
\end{minted}

这里\texttt{game\_moves}可能是一个数组、切片或者是vector，结果将是一个长度为\texttt{end - midpoint}的切片，在\texttt{second\_half}的生命周期内\texttt{game\_moves}都处于被借用的状态。

\texttt{..}运算符允许任意一边的操作数被省略，根据两边是否有操作数，它可以被划分为四种形式：
\begin{minted}{Rust}
    ..      // 全部范围
    a ..    // 起点范围 { start: a }
    .. b    // 终点范围 { end: b }
    a .. b  // 范围 { start: a, end: b }
\end{minted}

后两种形式是\emph{end-exclusive(右开区间)}：终点值将不会被包含。例如，范围\texttt{0 .. 3}包括数字\texttt{0}、\texttt{1}、\texttt{2}。

\texttt{..=}运算符产生\emph{end-inclusive(闭区间)}范围，它会包含终点值：
\begin{minted}{Rust}
    ..= b       // RangeToInclusive { end: b }
    a ..= b     // RangeInclusive::new(a, b)
\end{minted}

例如，范围\texttt{0 ..= 3}包含数字\texttt{0}、\texttt{1}、\texttt{2}、\texttt{3}。

只有包含start值的范围才可以迭代，因为一个循环必须有开始的地方。但在数组切片中，所有的六种形式都是有用的。如果起始值或终点值被省略了，将默认包含数组从起点开始的元素或者直到终点的元素。

因此快速排序的一个实现，经典的分治排序算法，将类似于这样：
\begin{minted}{Rust}
    fn quicksort<T: Ord>(slice: &mut [T]) {
        if slice.len() <= 1 {
            return; // 切片为空，不需要排序
        }

        // 将切片分为两个部分，前半部分和后半部分
        let pivot_index = partition(slice);

        // 递归排序`slice`的前半部分
        quicksort(&mut slice[.. pivot_index]);

        // 再排序后半部分
        quicksort(&mut slice[pivot_index + 1 ..]);
    }
\end{minted}

\section{引用运算符}

取地址运算符\texttt{\&}和\texttt{\&mut}，已经在\hyperref[ch05]{第5章}中介绍过了。

一元\texttt{*}运算符用于获取引用指向的值。正如我们看到的，当你使用\texttt{.}运算符访问字段或者方法时，Rust会自动解引用。因此只有在我们想读取或写入引用指向的整个值时，\texttt{*}运算符才是必须的。

例如，有时迭代器会产生引用，但程序需要底层的类型：
\begin{minted}{Rust}
    let padovan: Vec<u64> = compute_padovan_sequence(n);
    for elem in &padovan {
        draw_triangle(turtle, *elem);
    }
\end{minted}

在这个例子中，\texttt{elem}的类型是\texttt{\&u64}，因此\texttt{*elem}是一个\texttt{u64}值。

\section{算术、位运算、比较、逻辑运算符}

Rust的二元运算符和其他语言中的很像。为了节省时间，我们假设你熟悉这些语言中的一种，并只专注于Rust中背离传统的点。

Rust有通常的算术运算符\texttt{+, -, *, /, \%}。正如在\hyperref[ch03]{第3章}中说过的一样，整数溢出会被检测到，并在debug模式下造成panic。标准库提供类似\texttt{a.wrapping\_add(b)}的方法来执行不检查溢出的算术。

整数除法会向0舍入，并且整数除以0时即使在release模式也会导致panic。整数有一个\texttt{a.checked\_div(b)}方法会返回\texttt{Option}(如果\texttt{b}是0时为\texttt{None})，而不会panic。

一元的\texttt{-}运算符用于求负数，它支持除无符号整数外所有的数字类型。没有一元的\texttt{+}运算符：
\begin{minted}{Rust}
    println!("{}", -100);       // -100
    println!("{}", -100u32);    // 错误：一元`-`不能用于类型`u32`
    println!("{}", +100);       // 错误：期望表达式，发现`+`
\end{minted}

和在C中一样，\texttt{a \% b}会计算有符号的余数，或者叫取模。结果和左侧的操作数符号一致。注意\texttt{\%}也可以像整数一样用于浮点数：
\begin{minted}{Rust}
    let x = 1234.567 % 10.0;    // 约为4.567
\end{minted}

Rust还继承了C的整数的位运算符\texttt{\&, |, \^{}, <<, >>}。然而，Rust使用\texttt{!}而不是\texttt{\~{}}来表示按位取反：
\begin{minted}{Rust}
    let hi: u8 = 0xe0;
    let lo = !hi;   // 0x1f
\end{minted}

这意味着\texttt{!n}不能用来判断\texttt{n}是否为0，必须使用\texttt{n == 0}。

有符号整数的位移总是符号扩展，无符号整数的位移的位移总是0扩展。因为Rust有无符号整数，所以不需要像Java中的\texttt{>>>}一样的无符号移位运算符。

和C语言不同的是，位运算符比比较运算的优先级更高，因此如果你写\texttt{x \& BIT != 0}，意味着\texttt{(x \& BIT) != 0}。这比C中的含义更有用，C中将是\texttt{x \& (BIT != 0)}。

Rust的比较运算符是\texttt{==, !=, <, <=, >, >=}。比较的两个值类型必须相同。

Rust也有短路求值的\texttt{\&\&}和\texttt{||}运算符，所有的操作数必须都是\texttt{bool}类型。

\section{赋值}

\texttt{=}运算符可以用于给\texttt{mut}变量或它们的字段或元素赋值。但在Rust中赋值行为并不像在其他语言中那么普遍，因为变量默认是不可变的。

正如在\hyperref[ch04]{第4章}介绍的那样，如果变量是non-\texttt{Copy}类型，赋值行为将会把它的值\emph{移动}到目标变量。值的所有权从源对象移动到目的对象，目的对象之前的值会被丢弃。

也可以使用复合赋值：
\begin{minted}{Rust}
    total += item.price;
\end{minted}

这等价于\texttt{total = total + item.price;}。还支持其他的运算符例如：\texttt{-=, *=}等。完整的运算符支持在\hyperref[t6-1]{表6-1}中列出。

和C不同，Rust不支持链式赋值：你不能写\texttt{a = b = 3}来把\texttt{3}同时赋给\texttt{a}和\texttt{b}。在Rust中赋值很罕见，所以你不会怀念这种缩写的。

Rust不支持C的自增和自减运算符\texttt{++}和\texttt{--}。

\section{类型转换}\label{cast}

在Rust中把一个类型转换为另一个类型通常需要显式的转换。使用\texttt{as}关键字来进行转换：
\begin{minted}{Rust}
    let x = 17;             // x是i32类型
    let index = x as usize; // 转换为usize
\end{minted}

Rust允许以下几种转换：
\begin{itemize}
    \item 任何内建的数字类型可以彼此转换。
    
    将一个整数转换为另一个整数总是良定义的。转换为更小的类型会导致截断，有符号数转换为更大的类型会进行符号扩展，无符号数是0扩展，等等。总而言之，没有意外的行为。

    浮点数转换为整数会向0舍入：值\texttt{-1.99}转换为\texttt{i32}是\texttt{-1}。如果值太大不能用整数类型表示，转换会返回整数类型能表示的最接近真实值的值：值\texttt{1e6}转换为\texttt{u8}是\texttt{255}。

    \item \texttt{bool}、\texttt{char}、或者类似C的\texttt{enum}类型可以转换为任意整数类型（我们将在\hyperref[ch10]{第10章}中介绍枚举）。
    
    其他方向的转换是不允许的，因为\texttt{bool}、\texttt{char}和\texttt{enum}都对它们的值有严格的要求，必须要进行运行时检查。例如，将一个\texttt{u16}数字转换为\texttt{char}类型是禁止的，因为一些\texttt{u16}值例如\texttt{0xd800}，对应Unicode的代理码点，因此不是有效的\texttt{char}类型值。有一个标准的方法\texttt{std::char::from\_u32()}来进行运行时检查并返回\texttt{Option<char>}。但需要指出的是，这种转换的需求非常少见。我们通常一次转换整个字符串或流，有关Unicode文本的算法通常是非平凡的，最好留给库来实现。

    一个例外是，\texttt{u8}可以转换为\texttt{char}类型，因为0到255之间的所有整数都是有效的Unicode\\
    码点。

    \item 一些涉及unsafe指针类型的转换也是允许的。见\hyperref[rawp]{原始指针}一节。
\end{itemize}

我们说转换\emph{通常}需要显式的转换。但少数涉及引用类型的转换非常直观以至于语言可以自动完成而不需要显式转换。一个小例子是把\texttt{mut}引用转换为non-\texttt{mut}引用。

还有一些更重要的自动转换可能发生：
\begin{itemize}
    \item \texttt{\&String}类型的值可以自动转换为\texttt{\&str}类型。
    \item \texttt{\&Vec<i32>}类型的值可以自动转换为\texttt{\&[i32]}。
    \item \texttt{\&Box<Chessboard>}类型的值可以自动转换为\texttt{\&Chessboard}。
\end{itemize}

这些被称为\emph{强制解引用(deref coercions)}，因为它们适用于实现了内建的\texttt{Deref} trait的类型。\texttt{Deref}是为了智能指针类型设计的，例如\texttt{Box}，它的行为尽量和和底层的值类型保持一致。得益于\texttt{Deref}，使用\texttt{Box<Chessboard>}非常像在使用一个普通的\texttt{Chessboard}。

用户自定义的类型也可以实现\texttt{Deref} trait。当你需要编写自己的智能指针类型时，见\hyperref[deref]{Deref和DerefMut}一节。

\section{闭包}
Rust支持\emph{闭包}：一种轻量的类似函数的值。一个闭包通常由被竖线包围的参数列表和一个表达式组成：
\begin{minted}{Rust}
    let is_even = |x| x % 2 == 0;
\end{minted}

Rust会自动推导参数类型和返回值类型。你可以显式写出它们，就像写函数签名一样。如果你指定了返回值类型，那么为了语法的健全，闭包的主体必须是一个块：
\begin{minted}{Rust}
    let is_even = |x: u64| -> bool x % 2 == 0;  // error
    
    let is_even = |x: u64| -> bool { x % 2 == 0 };  // ok
\end{minted}

调用一个闭包和调用函数的语法相同：
\begin{minted}{Rust}
    assert_eq!(is_even(14), true);
\end{minted}

闭包是Rust中最令人愉快的特性之一，关于它有很多内容可以说，我们将在\hyperref[ch14]{第14章}中介绍。

\section{继续}

表达式是“要运行的代码(running code)”。它们是Rust中要被编译为机器指令的一部分。然而它们只是整个语言的一小部分。

大多数其他语言也是这样。一个程序的第一个任务是运行起来，但这并不是它唯一的任务。程序之间必须交流，它们也必须可以测试，它们还需要保持有序和灵活以便继续改进，它们必须与其它队伍构建的代码和服务进行互操作。而且即使只是为了运行，像Rust这样的典型的静态语言的程序也需要更多的工具来组织数据。

接下来，我们将用数个章节讨论这些领域：让你的程序变得结构化的模块和crate、让你的数据变得结构化的结构体和枚举。

首先，我们将花费一些篇幅来介绍一个非常重要的话题：错误处理。
